/*
 * CommandLineProcessor.cpp
 *
 *  Created on: 19/03/2011
 *      Author: ehpalumbo
 */

#include "CommandLineProcess.h"

#include <memory>

namespace CommandLineProcess {


// Tag

Tag::Tag(string& tagName, bool required) {
	this->required = required;
	this->tagName = &tagName;
}

bool Tag::isRequired() {
	return this->required;
}

string& Tag::getName() {
	return *(this->tagName);
}

Tag::~Tag() {
//	delete this->tagName;
}


// ArgumentHandler

ArgumentHandler::ArgumentHandler(Tag& aTag, string& aDescription) {
	this->tag = &aTag;
	this->description = &aDescription;
}

Tag& ArgumentHandler::getTag() {
	return *(this->tag);
}

string& ArgumentHandler::getDescription() {
	return *(this->description);
}

ArgumentHandler::~ArgumentHandler() {
//	delete this->tag;
//	delete this->description;
}


// CommandLineProcessor

CommandLineProcessor::CommandLineProcessor(ostream& output) {
	this->output = &output;
	this->handlerByTagNameMap = new map<string, ArgumentHandler*>();
	this->requiredTagNames = new set<string>();
}

void CommandLineProcessor::registerArgumentHandler(ArgumentHandler& handler) {
	string* theTagName = &(handler.getTag().getName());

	this->handlerByTagNameMap->insert(
			pair<string, ArgumentHandler*>(*new string(*theTagName), &handler));
	if (handler.getTag().isRequired()) {
		this->requiredTagNames->insert(*new string(*theTagName));
	}
}


bool isKey(const char* anArgument) {
	return (*anArgument == '-');
}

bool CommandLineProcessor::processArguments(int argc, const char* argv[]) {
	string theKey;
	string theValues;
	// Map entered keys with values, as CSV.
	map<string, string> foundKeyValues;
	bool isOkToProceed = true;

	//check if there are no args
	if (argc == 1){
		this->onNotParameters();
		return false;
	}

	theKey = "";
	theValues = "";
	for (int i = 1; i < argc; i++) {
		if (isKey(argv[i])) {
			// First insert previous key and its collected values, if any.
			if (theKey != "") {
				foundKeyValues.insert(pair<string, string>(
						*new string(theKey), *new string(theValues)));
			}
			theKey = argv[i];
			theValues = "";
		} else {
			if (theValues != "") theValues += ",";
			theValues += argv[i];

		}
	}

	if (theKey != "") {
		foundKeyValues.insert(pair<string, string>(
				*new string(theKey), *new string(theValues)));
	} else {
		if (theValues != ""){
			this->onUnknownParameter(theValues);
			return false;
		}
	}

	// Search undefined but required tags.
	bool foundRequiredNotProvided = false;
	for (set<string>::iterator it = this->requiredTagNames->begin();
			it != this->requiredTagNames->end() && !foundRequiredNotProvided;
			it++) {
		map<string, string>::iterator itArgs = foundKeyValues.find(*it);
		foundRequiredNotProvided = (itArgs == foundKeyValues.end());
	}

	// Print help if there's not a required parameter.
	if (foundRequiredNotProvided) {
		isOkToProceed = false;
		onNotEnoughParameters();
	} else {
		// Try to delegate arguments to handlers.
		for (map<string, string>::iterator it = foundKeyValues.begin();
				it != foundKeyValues.end() && isOkToProceed; it++) {
			map<string, ArgumentHandler*>::iterator itHandlers =
					this->handlerByTagNameMap->find((*it).first);
			if (itHandlers != this->handlerByTagNameMap->end()) {
				isOkToProceed = (*itHandlers).second->handleArgument((*it).second);
			} else {
				isOkToProceed = false;
				onUnknownParameter((*it).first);
			}
		}
	}
	foundKeyValues.clear();
	return isOkToProceed;
}

void CommandLineProcessor::printHelp() {
	*(this->output) << endl << "These are all the possible configuration parameters "
			<< "accepted by the application:" << endl << endl;
	// TODO: Print information about registered handlers.
	for (map<string,ArgumentHandler*>::iterator it = this->handlerByTagNameMap->begin();
			it != this->handlerByTagNameMap->end(); it++) {
		*(this->output) << "Tag '" << (*it).second->getTag().getName() << "': "
				<< (*it).second->getDescription() << endl;
	}
	*(this->output) << endl;
}

void CommandLineProcessor::onNotEnoughParameters() {
	*(this->output) << endl << "You must provide all the required parameters "
			<< "in order to use this application.-" << endl;
	printHelp();
}

void CommandLineProcessor::onNotParameters() {
	*(this->output) << endl << "You must provide parameters "
			<< "in order to use this application.-" << endl;
	printHelp();
}

void CommandLineProcessor::onUnknownParameter(const string& parameter) {
	*(this->output) << endl << "Can't execute task. Unknown configuration parameter: '"
			<< parameter << "'.-" << endl;
	printHelp();
}


CommandLineProcessor::~CommandLineProcessor() {
//	delete this->handlerByTagNameMap;
//	this->requiredTagNames->clear();
//	delete this->requiredTagNames;
}

}
